package cn.tedu.v2_1;
/**
 * 通过测试：ping通对方的IP   多人聊天室  知道谁是谁【IP】
 * Version2.1 多人聊天类
 * 功能：①知道客户端IP，打开服务器处
 * 启动服务器----N多客户端链接服务器---accept【返回一个客户端Socket】
 * 缺点：只知道；来人IP，并不知道是哪个IP在服务器端说话
 * <p>
 * 调整：获取IP地址的方法，移动--CS 多线程中处理
 * <p>
 * 思路：1.删除  N  多客户端链接服务器--打印IP的消息【证明链接上】
 * <p>
 * 2.获取IP信息--存入线程CS类中：
 * 处理多个客户端链接是否返回【客户端：】
 * CS构造器-->start方法---创建线程对象--传入客户端socket
 * 并且赋值当前线程socket对象
 * 【保证一个客户端链接服务器，正确返回，等同于找到正确客户端】
 * ①私有化   ip，目的：保证ip信息准确，供当前CS类所用
 * ②构造器：完成ip的地址获取方式：创建CS线程，明确客户socket
 * 立即给客户端进行IP赋值
 * socket.getInetAddress()    获取主机的ip信息对象
 * socket.getInetAddress.getHostAddress()  获取主机IP字符串
 * 服务器  显示   客户端内容，打印效果如下
 * 192.168.166.8的客户端说：
 * -----------------------------------------------------------------------------------------------
 * 优化操作：针对【SeverSocket类与socket  程序健壮性】
 * SeverSocket   运行在服务器socket通讯类--关闭问题
 * 关闭方法：服务器端main、start方法中finally关闭
 * 为什么在以上两个位置关闭，服务器通讯类：
 * 因为关闭后将无法在接收任何新的客户端的链接【所有功能提供完毕】
 * <p>
 * isClosed() 判断SeverSocket(Socket)对象是否关闭
 * 默认情况：true false表示没有关闭
 * CS线程类：处理各个客户端
 * CS线程中  Socket通讯类【同IO流操作同在】       --关闭问题
 * 判断逻辑：socket！=null     对象是否被赋值了
 *                           缺点：爆出空指针异常
 *         !socket.isClosed  对象没有被关闭
 *  socket！=null&&！socket.isClosed    解释如下：
 *  socket已经被赋值并且socket没有关闭--需要关闭
 *  关闭位置：流对应的finally块中
 *
 */

import java.io.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

public class Server {
    private ServerSocket serverSocket;

    public Server() {
        try {
            serverSocket = new ServerSocket(1234);
        } catch (BindException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        Socket socket = null;
        //关闭socket
        String ip = null;
        try {
            while (true) {
                System.out.println("等待客户端的连接");
                socket = serverSocket.accept();
                ip = socket.getInetAddress().getHostAddress();
                System.out.println("客户端：" + ip + "连接上了");
                new Thread(new CS(socket)).start();
            }
        } catch (IOException e) {
            System.out.println(ip + "客户端链接失效~~");
        } finally {
            //1.正确关闭severSocket--->start   方法中
            //不为空：是否初始化   尚未关闭：检查对象是否关闭
            if (serverSocket != null && !serverSocket.isClosed()) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    System.out.println("服务器关闭失败~~");
                }
                System.out.println("服务器关闭成功~~");
            }
        }
    }
    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }
    //2.1创建内部类：服务器端处理各个客户端线程类   CS  目的：进行读入
    private class CS implements Runnable {

        private Socket socket;
        //一、客户端IP在说话同时打印服务器窗口中
        //1.在CS线程中，添加ip的私有属性【封装：】
        private String ip;
        public CS(Socket s) {
            //线程传入客户端socket
            this.socket = s;
            //3.CS线程对象：传socket对象同时完成IP赋值【当前客户端IP信息】
            ip = socket.getInetAddress().getHostAddress();
        }
        //2.2通过线程运行状态完成：读取客户端发来的文件
        @Override
        public void run() {
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF8"));
                while (true) {
                    String str = br.readLine();
                    System.out.println(ip + "客户端：" + str);
                }
            } catch (UnsupportedEncodingException e) {
                System.out.println("服务器端与客户端连接不一致");
            } catch (SocketException e) {
                System.out.println("");
            } catch (IOException e) {
                System.out.println("客户端链接失效");
            } finally {//先关闭流，在关闭socket
                if (br != null)
                    try {
                        br.close();
                    } catch (IOException e) {
                        System.out.println("读入流断开链接");
                    }
                //2.关闭CS多线程中--运行的socket通讯类
                if (socket!=null&&!socket.isClosed()){
                    System.out.println(ip+"一个客户端退出服务器链接");
                    try {
                        socket.close();
                    } catch (IOException e) {
                        System.out.println(ip+"一个客户端退出链接异常~~");
                    }
                }
            }
        }
    }
}
